#ifndef K_SOCKET_H
#define K_SOCKET_H
#pragma once

#include "K_Winsock.h"
#include "KDebug.h"
#include "KNamespace.h"
#include "KNoCopy.h"

class KIAddress;
class KIpAddress;
class KKey;

K_NAMESPACE_BEG(KDef)
class SocketContext;
K_NAMESPACE_END(KDef)

class KSocket : KNoCopy {
public:
    typedef KDef::SocketContext Context;
public:
    KSocket();
    virtual ~KSocket();
public:
    SOCKET              handle() const                          { return m_socket; }
    bool                isAlive() const                         { return handle() != INVALID_SOCKET; }
    virtual int         protocol() const                        { return 0; }
    virtual int         sockType() const                        { return 0; }
public:
    virtual Context *   getContext()                            { return NULL; }
    bool                getLocalAddress(KIpAddress &addrLocal) const;
    virtual bool        getRemoteAddress(KIpAddress &addrRemote) const;
    bool                prepareTransmission()                   { return initTransmitFile(); }
    bool                updateAcceptContextWith(const KSocket &acceptor);
    bool                updateConnectContext();
public:
    SOCKET              attach(SOCKET sock = INVALID_SOCKET);
    bool                bind(const KIAddress &addrLocal);
    void                close();
    SOCKET              detach()                                { return attach(); }
    bool                open(int sendSize = -1);
    bool                shutdown(int how = SD_BOTH);
public:
    bool                setBroadcast(bool enabled);
    int                 connectTime() const;
    bool                setKeepAlive(bool enabled, ULONG time = 0, ULONG interval = 0);
    KKey *              key() const                             { return m_key; }
    void                setKey(KKey *userKey)                   { m_key = userKey; }
    bool                setLinger(bool onoff, u_short sec = 0)  { return checkSetLinger(onoff, sec, false); }
    bool                noDelay() const;
    bool                setNoDelay(bool enabled);
    bool                setNonblock(bool enabled);
    DWORD               requestTimeOut() const                  { return m_timeOut; }
    void                setRequestTimeOut(DWORD ms = 0)         { m_timeOut = ms; }
    bool                setReuseAddr(bool enabled);
    int                 recvBufSize() const;
    bool                setRecvBufSize(DWORD size);
    int                 sendBufSize() const;
    bool                setSendBufSize(DWORD size);
public:
    static bool         initCancel();
    static bool         isTcpSocket(const KSocket &sock);
    static bool         isUdpSocket(const KSocket &sock);
    static bool         isUnspecific(const KSocket &sock);
protected:
    bool                checkSetLinger(bool onoff, u_short sec = 0, bool check = true);
    bool                initAcceptEx();
    bool                initConnectEx();
    bool                initDisconnectEx();
    bool                initTransmitFile();
protected:
    // These methods are not guaranteed safe
    BOOL                asyncAccept(KSocket *client, PVOID buf, LPOVERLAPPED request);
    bool                asyncCancel(LPOVERLAPPED request = NULL);
    BOOL                asyncConnect(const KIAddress &addr, LPOVERLAPPED request);
    BOOL                asyncDisconnect(LPOVERLAPPED request);
    virtual bool        asyncRecv(LPWSABUF pBuf, LPOVERLAPPED request);
    virtual bool        asyncSend(LPWSABUF pBuf, LPOVERLAPPED request);
    BOOL                asyncTransmit(HANDLE hFile, DWORD total, DWORD per, LPTRANSMIT_FILE_BUFFERS pBuf, LPOVERLAPPED request);
private:
    typedef BOOL (WINAPI *LPFN_CANCELIOEX)(HANDLE, LPOVERLAPPED);
    static LPFN_CANCELIOEX s_cancel;
private:
    DWORD               m_timeOut;
    KKey *              m_key;
    LPFN_ACCEPTEX       m_accept;
    LPFN_CONNECTEX      m_connect;
    LPFN_DISCONNECTEX   m_disconnect;
    LPFN_TRANSMITFILE   m_transmit;
    SOCKET              m_socket;
    friend class        KMultiplexor;
};

#endif // K_SOCKET_H